﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Serialization;
using InnovTech.Util.Crypto;
using InnovTech.Util.IO;

namespace InnovTech.Util.Xml
{
    /// <summary>
    /// Presents a xml serializer with crypto. The serialized xml will be encrypted and translated in a Base64 string.
    /// </summary>
    public class CryptoXmlSerializer
    {
        private XmlSerializer _serializer;
        private SymmetricAlgorithm _crypto;

        public CryptoXmlSerializer(SymmetricAlgorithm crypto)
        {
            _serializer = new XmlSerializer(typeof(string));
            _crypto = crypto;
        }

        /// <summary>
        /// Serializes the target object into the given output Stream
        /// </summary>
        /// <param name="output"></param>
        /// <param name="target"></param>
        public void Serialize(Stream output, object target)
        {
            if(target == null)
                throw new ArgumentNullException("The target to serialize can not be null");
            if(output == null)
                throw new ArgumentNullException("The output stream can not be null");

            string toSerialize = null;
            XmlSerializer ser = new XmlSerializer(target.GetType());
            using(MemoryStream tmp = new MemoryStream())
            {
                //serialize the xml to the temp stream.
                ser.Serialize(tmp, target);
                tmp.Seek(0, SeekOrigin.Begin);
                //get the xml text form the temp stream.
                toSerialize = StreamUtil.GetStringFromMemoryStream(tmp);
            }
            //encrypt the xml text
            toSerialize = CryptoUtil.EncryptToBase64String(toSerialize, _crypto);
            //serialize the encrypted xml texted into the target stream.
            this._serializer.Serialize(output, toSerialize);
        }

        /// <summary>
        /// Deserializes the content of the given input Stream to an object with the given Type.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public object Deserialize(Stream input, Type targetType)
        {
            //deserialize the input.
            string base64 = _serializer.Deserialize(input) as string;
            //decrypt the base64 string to the xml text.
            string toDeserialize = CryptoUtil.DecryptFromBase64String(base64, _crypto);
            XmlSerializer ser = new XmlSerializer(targetType);
            //put the xml text into a MemoryStream
            using (MemoryStream ms = StreamUtil.CreateStringMemoryStream(toDeserialize))
            {
                //deserilaize the xml text from the temp stream to an object.
                return ser.Deserialize(ms);
            }
        }

    }
}
